package org.witsoft.demo.graphql.extend.relay;

import graphql.relay.*;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.function.Function;

@RequiredArgsConstructor
public class ConnectionFactory<T> {

    private final Function<T, String> keyFunction;

    private final Function<ConnectionRequest, List<T>>  valueFunction;

    private Function<ConnectionRequest, Boolean>  hasNextPageFunction;

    private String buildCursor(long offset, String key){
        byte[] encode = (offset + "$" + key).getBytes();
        return Base64.getEncoder().encodeToString(encode);
    }

    public Connection<T> nextConnection(int size, String lastCursor){
        long offset = 0;
        String key = null;
        if(!StringUtils.isAllEmpty(lastCursor)){
            byte[] decode = Base64.getDecoder().decode(lastCursor);
            String[] input = new String(decode).split("[$]");
            offset = Long.parseLong(input[0]);
            key = input[1];
        }
        ConnectionRequest request = ConnectionRequest.builder ()
                .offset(offset)
                .key(key)
                .size(size)
                .lastCursor(lastCursor)
                .build();
        List<T> items = valueFunction.apply(request);
        boolean hasNextPage = !items.isEmpty();
        if(hasNextPage){
            if(hasNextPageFunction != null){
                hasNextPage = hasNextPageFunction.apply(request);
            }else {
                request.setOffset(request.getOffset() + items.size());
                request.setKey(keyFunction.apply(items.get(items.size() - 1)));
                request.setLastCursor(buildCursor(request.getOffset(), request.getKey()));
                request.setSize(1);
                List<T> nextItems = valueFunction.apply(request);
                hasNextPage = !nextItems.isEmpty();
            }
        }
        return newConnection(items, request.getOffset(), hasNextPage);
    }

    private Connection<T> newConnection(List<T> list, long startOffset, boolean hasNextPage){
        long cursorOffset = startOffset;
        List<Edge<T>> edges = new ArrayList<>(list.size());
        for (int i = 0; i < list.size(); i++) {
            cursorOffset += 1;
            T item = list.get(i);
            String key = keyFunction.apply(item);
            String cursorStr = buildCursor(cursorOffset, key);
            ConnectionCursor cursor = new DefaultConnectionCursor(cursorStr);
            edges.add(new DefaultEdge<T>(item , cursor));
        }
        ConnectionCursor startCursor = null;
        ConnectionCursor endCursor = null;
        if(!edges.isEmpty()){
            startCursor = edges.get(edges.size()-1).getCursor();
            endCursor = edges.get(edges.size()-1).getCursor();
        }
        boolean hasPreviousPage = cursorOffset > 0;
        PageInfo pageInfo = new DefaultPageInfo(startCursor, endCursor, hasPreviousPage, hasNextPage);
        return new DefaultConnection<T>(edges, pageInfo);
    }
}
